package com.witmore.skyline.web.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.witmore.skyline.db.entity.*;
import com.witmore.skyline.db.mapper.*;
import com.witmore.skyline.db.mapper.dto.DeviceCategoryResultDTO;
import com.witmore.skyline.db.mapper.dto.DeviceListQueryDTO;
import com.witmore.skyline.db.mapper.dto.DeviceMapStatisticsResultDTO;
import com.witmore.skyline.db.mapper.dto.DeviceMonthlyUsageRateDTO;
import com.witmore.skyline.web.config.BusinessConfig;
import com.witmore.skyline.web.controller.enums.WitmoreErrorCode;
import com.witmore.skyline.web.controller.vo.param.*;
import com.witmore.skyline.web.controller.vo.result.*;
import com.witmore.skyline.web.service.*;
import com.witmore.skyline.web.service.third.yjx.client.ApiCameraClient;
import com.witmore.skyline.web.service.third.yjx.client.ApiDeviceClient;
import com.witmore.skyline.web.service.third.yjx.client.ApiDeviceDataClient;
import com.witmore.skyline.web.service.third.yjx.dto.*;
import com.witmore.skyline.web.utils.AddressParserUtils;
import com.witmore.skyline.web.utils.LocalDateUtils;
import com.witmore.skyline.web.utils.mapworld.MapWorldReverseGeocoding;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.witmore.skyline.web.constants.TableSortConstant.ALLOWED_SORT_ORDER;
import static com.witmore.skyline.web.constants.TableSortConstant.DEVICE_ALLOWED_SORT_FIELDS;
import static com.witmore.skyline.web.utils.AddressParserUtils.parseAddress;

/**
 * Description: DeviceServiceImpl
 * Author: 豆子高
 * Date: 2025/2/25 10:14
 */
@Slf4j
@Service
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private ApiDeviceClient apiDeviceClient;

    @Autowired
    private ApiDeviceDataClient apiDeviceDataClient;

    @Autowired
    private ApiCameraClient apiCameraClient;
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceDailyReportMapper deviceDailyReportMapper;

    @Autowired
    private BusinessConfig businessConfig;

    @Autowired
    private CameraService cameraService;

    @Autowired
    private CameraMapper cameraMapper;

    @Autowired
    private DriverMapper driverMapper;

    @Autowired
    private OrgMapper orgMapper;

    @Autowired
    private ProjectService projectService;

    @Autowired
    private DeviceOilService deviceOilService;

    @Autowired
    private DeviceTimeService deviceTimeService;


    @Autowired
    private CameraPicMapper cameraPicMapper;

    @Autowired
    private ThreadPoolExecutor deviceSyncExecutor;

    @Autowired
    private ThreadPoolExecutor apiCallExecutor;

    @Override
    public void getAndSaveDeviceListFromCloudm() {
        int page = 1;
        int size = 20;
        while (true) {
            // 请求当前页数据
            List<CloudmDeviceDTO> devices;
            try {
                devices = apiDeviceClient.fetchDeviceList(page, size);
                if (CollectionUtils.isEmpty(devices)) {
                    break;
                }
            } catch (Exception e) {
                log.error("设备列表信息拉取失败 error msg-->{},page-->{},size-->{}, e",
                        e.getMessage(), page, size, e);
                break;
            }
            // 处理获取的数据 入库
            try {
                this.saveDeviceList(devices);
            } catch (Exception e) {
                log.error("设备列表信息保存失败 error msg-->{},page-->{},size-->{}, e",
                        e.getMessage(), page, size, e);
                break;
            }
            // 翻到下一页
            page++;
        }
    }

    @Override
    public void deviceFieldMaintenanceFromCloudm(String deviceSn) {
        log.info("开始设备详情同步，deviceSn: {}", deviceSn);
        long startTime = System.currentTimeMillis();
        int totalDevices = 0;
        int successDevices = 0;
        int failedDevices = 0;

        int page = 1;
        int size = 20;  // 减少每页数量，降低内存压力

        while (true) {
            Page<DeviceDO> deviceDOPage = deviceMapper.selectPage(new Page<>(page, size),
                new LambdaQueryWrapper<DeviceDO>()
                    .eq(DeviceDO::getIsDeleted, 0)
                    .eq(ObjectUtils.isNotEmpty(deviceSn), DeviceDO::getDeviceSn, deviceSn)
            );

            if (CollectionUtils.isEmpty(deviceDOPage.getRecords())) {
                break;
            }

            log.info("处理第{}页设备，共{}台设备", page, deviceDOPage.getRecords().size());
            totalDevices += deviceDOPage.getRecords().size();

            // 并发处理当前页的设备
            int[] batchResult = processDeviceBatch(deviceDOPage.getRecords());
            successDevices += batchResult[0];
            failedDevices += batchResult[1];

            // 批次间隔，让系统喘息
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("设备同步被中断");
                break;
            }

            page++;
        }

        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;

        log.info("设备详情同步完成！总设备数: {}, 成功: {}, 失败: {}, 耗时: {}ms ({}秒)",
            totalDevices, successDevices, failedDevices, duration, duration / 1000.0);
    }

    /**
     * 批量处理设备 - 设备级并发
     * @param deviceList 设备列表
     * @return [成功数量, 失败数量]
     */
    private int[] processDeviceBatch(List<DeviceDO> deviceList) {
        List<CompletableFuture<Boolean>> deviceFutures = deviceList.stream()
            .map(deviceDO -> CompletableFuture.supplyAsync(() -> {
                try {
                    processSingleDevice(deviceDO);
                    return true;
                } catch (Exception e) {
                    log.error("设备处理失败: deviceSn={}, error={}",
                        deviceDO.getDeviceSn(), e.getMessage(), e);
                    return false;
                }
            }, deviceSyncExecutor))
            .toList();

        // 等待当前批次所有设备处理完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
            deviceFutures.toArray(new CompletableFuture[0])
        );

        try {
            // 设置超时时间，避免无限等待
            allFutures.get(300, TimeUnit.SECONDS); // 5分钟超时
        } catch (Exception e) {
            log.error("批量设备处理超时或异常", e);
        }

        // 统计成功和失败数量
        int successCount = 0;
        int failedCount = 0;

        for (CompletableFuture<Boolean> future : deviceFutures) {
            try {
                if (future.get()) {
                    successCount++;
                } else {
                    failedCount++;
                }
            } catch (Exception e) {
                failedCount++;
            }
        }

        return new int[]{successCount, failedCount};
    }

    /**
     * 处理单个设备 - 优化版：任务内部串行处理
     * @param deviceDO 设备信息
     */
    private void processSingleDevice(DeviceDO deviceDO) {
        log.debug("开始处理设备: {}", deviceDO.getDeviceSn());

        // 任务A: 设备相关的完整流程 (API调用 + 数据库更新串行)
        CompletableFuture<Void> deviceTask = CompletableFuture.runAsync(() -> {
            processDeviceCompleteFlow(deviceDO);
        }, apiCallExecutor);

        // 任务B: 摄像头相关的完整流程 (API调用 + 数据库更新串行)
        CompletableFuture<Void> cameraTask = CompletableFuture.runAsync(() -> {
            processCameraCompleteFlow(deviceDO);
        }, apiCallExecutor);

        // 等待两个完整流程完成
        try {
            CompletableFuture.allOf(deviceTask, cameraTask).get(60, TimeUnit.SECONDS);
            log.debug("设备处理完成: {}", deviceDO.getDeviceSn());

        } catch (Exception e) {
            log.error("设备同步失败: deviceSn={}", deviceDO.getDeviceSn(), e);
            throw new RuntimeException("设备同步失败: " + deviceDO.getDeviceSn(), e);
        }
    }

    /**
     * 任务A: 设备相关的完整流程 (API调用 + 数据库更新串行)
     * @param deviceDO 设备信息
     */
    private void processDeviceCompleteFlow(DeviceDO deviceDO) {
        try {
            log.debug("开始设备完整流程: {}", deviceDO.getDeviceSn());

            // 1. 调用设备详情API
            log.debug("调用设备详情API: {}", deviceDO.getDeviceSn());
            CloudmDeviceDTO cloudmDeviceDTO = apiDeviceClient.fetchDeviceSingle(deviceDO.getDeviceSn());

            if (cloudmDeviceDTO == null) {
                log.warn("设备详情API返回空数据，跳过设备更新: {}", deviceDO.getDeviceSn());
                return;
            }

            // 2. 地理编码处理
            String addressByCoordinates = deviceDO.getCurrentAddr();
            if (needGeocoding(deviceDO, cloudmDeviceDTO)) {
                try {
                    log.debug("执行地理编码: deviceSn={}, lng={}, lat={}",
                        deviceDO.getDeviceSn(), cloudmDeviceDTO.getLng(), cloudmDeviceDTO.getLat());

                    addressByCoordinates = MapWorldReverseGeocoding.reverseGeocode(
                        cloudmDeviceDTO.getLng(), cloudmDeviceDTO.getLat());

                    log.debug("地理编码完成: deviceSn={}, address={}",
                        deviceDO.getDeviceSn(), addressByCoordinates);

                } catch (Exception e) {
                    log.error("地理编码失败，使用原地址: deviceSn={}, lng={}, lat={}, error={}",
                        deviceDO.getDeviceSn(), cloudmDeviceDTO.getLng(),
                        cloudmDeviceDTO.getLat(), e.getMessage());
                    // 地理编码失败不影响设备信息更新，使用原地址
                }
            }

            // 3. 立即更新设备信息到数据库
            log.debug("更新设备信息到数据库: {}", deviceDO.getDeviceSn());
            this.updateDeviceInfo(deviceDO, cloudmDeviceDTO, addressByCoordinates);
            log.debug("设备信息更新完成: {}", deviceDO.getDeviceSn());

        } catch (Exception e) {
            log.error("设备完整流程失败: deviceSn={}, error={}",
                deviceDO.getDeviceSn(), e.getMessage(), e);
            throw new RuntimeException("设备完整流程失败: " + e.getMessage(), e);
        }
    }

    /**
     * 任务B: 摄像头相关的完整流程 (API调用 + 数据库更新串行)
     * @param deviceDO 设备信息
     */
    private void processCameraCompleteFlow(DeviceDO deviceDO) {
        try {
            log.debug("开始摄像头完整流程: {}", deviceDO.getDeviceSn());

            // 1. 调用摄像头列表API
            log.debug("调用摄像头列表API: {}", deviceDO.getDeviceSn());
            List<CameraDTO> cameraDTOS = apiCameraClient.fetchCameraList(deviceDO.getDeviceSn());

            if (CollectionUtils.isEmpty(cameraDTOS)) {
                log.debug("摄像头列表为空，跳过摄像头更新: {}", deviceDO.getDeviceSn());
                return;
            }

            log.debug("获取到摄像头列表: deviceSn={}, count={}",
                deviceDO.getDeviceSn(), cameraDTOS.size());

            // 2. 立即更新摄像头信息到数据库
            log.debug("更新摄像头信息到数据库: deviceSn={}, count={}",
                deviceDO.getDeviceSn(), cameraDTOS.size());
            cameraService.getAndSaveCameraList(deviceDO, cameraDTOS);
            log.debug("摄像头信息更新完成: deviceSn={}, count={}",
                deviceDO.getDeviceSn(), cameraDTOS.size());

        } catch (Exception e) {
            log.error("摄像头完整流程失败: deviceSn={}, error={}",
                deviceDO.getDeviceSn(), e.getMessage(), e);
            // 摄像头流程失败不影响设备流程，只记录日志
            // 不抛出异常，避免影响整个设备处理
        }
    }


    /**
     * 判断是否需要进行地理编码
     * @param deviceDO 数据库中的设备信息
     * @param cloudmDeviceDTO 云机械返回的设备信息
     * @return 是否需要地理编码
     */
    private boolean needGeocoding(DeviceDO deviceDO, CloudmDeviceDTO cloudmDeviceDTO) {
        // 设备没有经纬度信息，但云机械有
        if (deviceDO.getLng() == null || deviceDO.getLat() == null) {
            return cloudmDeviceDTO.getLng() != null && cloudmDeviceDTO.getLat() != null;
        }

        // 经纬度发生变化
        if (cloudmDeviceDTO.getLat() != null && cloudmDeviceDTO.getLng() != null) {
            return !deviceDO.getLng().equals(cloudmDeviceDTO.getLng()) ||
                   !deviceDO.getLat().equals(cloudmDeviceDTO.getLat());
        }

        return false;
    }


    /**
     * 更新设备信息（原updateDevice方法的核心逻辑）
     * @param deviceDO 原设备信息
     * @param cloudmDeviceDTO 云机械设备信息
     * @param addressByCoordinates 地理编码地址
     */
    private void updateDeviceInfo(DeviceDO deviceDO, CloudmDeviceDTO cloudmDeviceDTO, String addressByCoordinates) {
        // 解析地址信息
        AddressParserUtils.AddressInfo addressInfo = parseAddress(addressByCoordinates, deviceDO.getDeviceName());

        // 填充设备状态、工时、油位信息、地址信息
        DeviceDO build = DeviceDO.builder()
                .id(deviceDO.getId())
                .currentAddr(addressByCoordinates)
                .province(addressInfo == null ? null : addressInfo.getProvince())
                .city(addressInfo == null ? null : addressInfo.getCity())
                .district(addressInfo == null ? null : addressInfo.getDistrict())
                .lng(cloudmDeviceDTO.getLng())
                .lat(cloudmDeviceDTO.getLat())
                .workStatus(cloudmDeviceDTO.getWorkStatus())
                .workTime(cloudmDeviceDTO.getWorkTime())
                .oilLevel(cloudmDeviceDTO.getOilLevel())
                // 新增字段
                .dailyWorkTime(cloudmDeviceDTO.getDailyWorkTime())
                .collectionTime(cloudmDeviceDTO.getCollectionTime() != null ?
                    LocalDate.parse(cloudmDeviceDTO.getCollectionTime()) : null)
                .runSpeed(cloudmDeviceDTO.getRunSpeed())
                .battery(cloudmDeviceDTO.getBattery())
                .mileage(cloudmDeviceDTO.getMileage())
                .oilPerMileage(cloudmDeviceDTO.getOilPerMileage())
                .targeting(cloudmDeviceDTO.getTargeting())
                .updateTime(LocalDateTime.now())
                .build();
        deviceMapper.updateById(build);
    }

    @Override
    public void updateDevice(DeviceDO deviceDO, CloudmDeviceDTO cloudmDeviceDTO) {
        // 根据经纬度获取详细地址
        String addressByCoordinates = deviceDO.getCurrentAddr();
        // 经纬度变化需要重新计算地理编码
        try {
            if (deviceDO.getLng() == null || deviceDO.getLat() == null ||
                    (cloudmDeviceDTO.getLat() != null && cloudmDeviceDTO.getLng() != null &&
                            !deviceDO.getLng().equals(cloudmDeviceDTO.getLng()) &&
                            !deviceDO.getLat().equals(cloudmDeviceDTO.getLat()))) {

                addressByCoordinates = MapWorldReverseGeocoding.reverseGeocode(cloudmDeviceDTO.getLng(), cloudmDeviceDTO.getLat());
            }
        } catch (Exception e) {
            log.error("根据经纬度获取详细地址失败 error msg-->{},deviceSn-->{}, e",
                    e.getMessage(), deviceDO.getDeviceSn(), e);
        }

        // 解析地址信息
        AddressParserUtils.AddressInfo addressInfo = parseAddress(addressByCoordinates, deviceDO.getDeviceName());

        // 填充设备状态、工时、油位信息、地址信息
        DeviceDO build = DeviceDO.builder()
                .id(deviceDO.getId())
                // TODO 不需要从云机械同步数据 20251022本次迭代稳定后删除
//                .category(cloudmDeviceDTO.getCategory())
                .currentAddr(addressByCoordinates)
                .province(addressInfo == null ? null : addressInfo.getProvince())
                .city(addressInfo == null ? null : addressInfo.getCity())
                .district(addressInfo == null ? null : addressInfo.getDistrict())
                .lng(cloudmDeviceDTO.getLng())
                .lat(cloudmDeviceDTO.getLat())
                .workStatus(cloudmDeviceDTO.getWorkStatus())
                .workTime(cloudmDeviceDTO.getWorkTime())
                .oilLevel(cloudmDeviceDTO.getOilLevel())
                // 新增字段
                .dailyWorkTime(cloudmDeviceDTO.getDailyWorkTime())
                .collectionTime(cloudmDeviceDTO.getCollectionTime() != null ?
                    LocalDate.parse(cloudmDeviceDTO.getCollectionTime()) : null)
                .runSpeed(cloudmDeviceDTO.getRunSpeed())
                .battery(cloudmDeviceDTO.getBattery())
                .mileage(cloudmDeviceDTO.getMileage())
                .oilPerMileage(cloudmDeviceDTO.getOilPerMileage())
                .targeting(cloudmDeviceDTO.getTargeting())
                .updateTime(LocalDateTime.now())
                .build();
        deviceMapper.updateById(build);
    }

    @Override
    public void deviceDailyFromCloudm(LocalDate startDay, LocalDate endDay, Integer skylineDeviceId) {
        if (null == startDay) {
            startDay = LocalDate.now().minusDays(1);
        }
        if (null == endDay) {
            endDay = LocalDate.now().minusDays(1);
        }
        if (startDay.isAfter(endDay)) {
            throw new RuntimeException("startDay不能大于endDay");
        }
        // 时间格式验证 endDay最晚为昨天
        if (startDay.isAfter(LocalDate.now().minusDays(1))) {
            throw new RuntimeException("startDay不能大于昨天");
        }
        if (endDay.isAfter(LocalDate.now().minusDays(1))) {
            throw new RuntimeException("endDay不能大于昨天");
        }
        int page = 1;
        int size = 50;
        while (true) {
            Page<DeviceDO> deviceDOPage = deviceMapper.selectPage(new Page<>(page, size), new LambdaQueryWrapper<DeviceDO>()
                    .eq(DeviceDO::getIsDeleted, 0)
                    .eq(ObjectUtils.isNotEmpty(skylineDeviceId), DeviceDO::getId, skylineDeviceId)
            );
            if (CollectionUtils.isEmpty(deviceDOPage.getRecords())) {
                break;
            }
            // LocalDate 转换成string
            String startDayStr = LocalDateUtils.dateConvertString(startDay);
            String endDayStr = LocalDateUtils.dateConvertString(endDay);
            LocalDate finalStartDay = startDay;
            LocalDate finalEndDay = endDay;
            deviceDOPage.getRecords().forEach(deviceDO -> {
                // 从云机械获取设备日报详情
                try {
                    // 总工时
                    this.totalWorkTimeByDay(startDayStr, endDayStr, deviceDO);
                    // 加油量 （云机械的加油记录）
                    this.totalRefuelByDay(finalStartDay, finalEndDay, deviceDO);
                    // 加油量 （宇墨自己算的加油记录）
                    this.totalRefuelByDayCustom(finalStartDay, finalEndDay, deviceDO);
                    // 用油量
                    this.totalOilConsumption(finalStartDay, finalEndDay, deviceDO);
                    // 最早开工时间-最晚工作结束时间
                    this.earlyAndLateWorkTime(finalStartDay, finalEndDay, deviceDO);
                    // 平均油耗
                    this.avgFuelConsumption(finalStartDay, finalEndDay, deviceDO);
                } catch (Exception e) {
                    log.error("设备日报数据操作失败 error msg-->{},deviceDO-->{}, e",
                            e.getMessage(), JSONObject.toJSONString(deviceDO), e);
                }
            });
            // 翻到下一页
            page++;
        }
    }

    @Override
    public ResponseResult<List<DeviceLocusDTO>> locusList(String deviceSn, LocalDate startDay, LocalDate endDay) {

        if (StringUtils.isEmpty(deviceSn) || null == startDay || null == endDay) {
            return ResponseResult.fail(WitmoreErrorCode.PARAM_ERROR);
        }
        // 校验时间格式 最多7天
        if (startDay.isAfter(endDay)) {
            return ResponseResult.fail(WitmoreErrorCode.START_DATE_AFTER_END_DATE);
        }
        if (ChronoUnit.DAYS.between(startDay, endDay) > 7) {
            return ResponseResult.fail(WitmoreErrorCode.DATE_INTERVAL_NOT_ALLOWED_7);
        }
        String startStr = LocalDateUtils.dateConvertString(startDay);
        String endStr = LocalDateUtils.dateConvertString(endDay);
        try {
            List<DeviceLocusDTO> deviceLocus = apiDeviceDataClient.getDeviceLocus(deviceSn, startStr, endStr);
            return ResponseResult.success(deviceLocus);
        } catch (Exception e) {
            log.error("设备经纬度信息获取失败 error msg-->{},deviceSn-->{},startDay-->{},endDay-->{}, e",
                    e.getMessage(), deviceSn, startDay, endDay, e);
            return ResponseResult.fail(WitmoreErrorCode.SYSTEM_ERROR.getCode(), e.getMessage());
        }
    }

    @Override
    public ResponsePageResult<DeviceListResultVO> deviceListPage(DeviceListParamVO deviceListParamVO) {
        // 根据项目部id查询设备id信息（现在直接从device表查询）
        List<Integer> businessIdListByProjectId = new ArrayList<>();
        if (null != deviceListParamVO.getProjectId()) {
            businessIdListByProjectId = projectService.getBusinessIdListByProjectId(deviceListParamVO.getOrgId(), deviceListParamVO.getProjectId(), 1);
            if (CollectionUtils.isEmpty(businessIdListByProjectId)) {
                // 当前登录的项目部id下无设备信息
                return ResponsePageResult.success(new PageInfo(1, 20, 0, 0));
            }
        }
        DeviceListQueryDTO queryDTO = new DeviceListQueryDTO();
        BeanUtils.copyProperties(deviceListParamVO, queryDTO);
        queryDTO.setIsDeleted(0);
        queryDTO.setDeviceIds(businessIdListByProjectId);
        if (StringUtils.isEmpty(queryDTO.getSortOrder()) || StringUtils.isEmpty(queryDTO.getSortField())) {
            // 默认排序：先按工作状态排序（负荷、怠速、在线、离线），再按今日工时倒序
            queryDTO.setSortField("work_status_custom");
            queryDTO.setSortOrder("DESC");
        }
        if (!ALLOWED_SORT_ORDER.contains(queryDTO.getSortOrder()) || !DEVICE_ALLOWED_SORT_FIELDS.contains(queryDTO.getSortField())) {
            // 如果传入无效排序参数，也使用默认排序
            queryDTO.setSortField("work_status_custom");
            queryDTO.setSortOrder("DESC");
        }
        // 总数量
        int count = deviceMapper.selectCustomCount(queryDTO);
        if (count == 0) {
            return ResponsePageResult.success(new PageInfo(1, 20, 0, 0));
        }
        Page<DeviceDO> deviceDOPage = deviceMapper.selectPageCustom(new Page<>(deviceListParamVO.getPage(), deviceListParamVO.getSize()), queryDTO);
        if (null == deviceDOPage) {
            return ResponsePageResult.fail(500, "设备列表查询失败");
        }
        if (CollectionUtils.isEmpty(deviceDOPage.getRecords())) {
            return ResponsePageResult.success(new PageInfo(deviceDOPage.getCurrent(), deviceDOPage.getSize(), deviceDOPage.getTotal(), deviceDOPage.getPages()));
        }
        List<DeviceListResultVO> resultVOS = new ArrayList<>();
        deviceDOPage.getRecords().forEach(deviceDO -> {
            DeviceListResultVO build = DeviceListResultVO.builder()
                    .id(deviceDO.getId())
                    .orgId(deviceDO.getOrgId())
                    .deviceSn(deviceDO.getDeviceSn())
                    .deviceName(deviceDO.getDeviceName())
                    .brand(deviceDO.getBrand())
                    .category(deviceDO.getCategory())
                    .model(deviceDO.getModel())
                    .workTime(deviceDO.getWorkTime())
                    .todayWorkTime(deviceDO.getTodayWorkTime())
                    .createTime(deviceDO.getCreateTime())
                    .currentAddr(deviceDO.getCurrentAddr())
                    .province(deviceDO.getProvince())
                    .city(deviceDO.getCity())
                    .district(deviceDO.getDistrict())
                    .lng(deviceDO.getLng())
                    .lat(deviceDO.getLat())
                    .workStatus(deviceDO.getWorkStatus())
                    .oilLevel(deviceDO.getOilLevel())
                    .fuelTankCapacity(deviceDO.getFuelTankCapacity())
                    .hasOilLevelModule(deviceDO.getHasOilLevelModule())
                    // 驾驶员相关信息
                    .driverId(deviceDO.getDriverId())
                    .driverName(deviceDO.getDriverName())
                    .driverPhone(deviceDO.getDriverPhone())
                    .bindTime(deviceDO.getBindTime())
                    .build();

            // 如果有绑定驾驶员，查询驾驶员详细信息
            if (deviceDO.getDriverId() != null) {
                DriverDO driver = driverMapper.selectById(deviceDO.getDriverId());
                if (driver != null && driver.getIsDeleted() == 0) {
                    build.setDriverPhoto(driver.getDriverPhoto());
                    build.setLicenseType(driver.getLicenseType());
                }
            }
            // 需要摄像头信息
            if (null != deviceListParamVO.getCameraNeed() && deviceListParamVO.getCameraNeed()) {
                List<CameraDO> cameraDO = cameraMapper.selectList(new LambdaQueryWrapper<CameraDO>()
                        .eq(CameraDO::getIsDeleted, 0)
                        .eq(CameraDO::getSkylineDeviceId, deviceDO.getId())
                );
                build.setCameraList(cameraDO);
            }
            // 填充项目信息
            List<ProjectDO> projectDOS = projectService.getProjectParentListByBusinessId(deviceDO.getId(), 1);
            if (!CollectionUtils.isEmpty(projectDOS)) {
                build.setProjectParentIdsList(projectDOS.stream().map(ProjectDO::getId).collect(Collectors.toList()));
                build.setProjectParentNames(projectDOS.stream().map(ProjectDO::getProjectName).collect(Collectors.toList()));
                build.setProjectId(projectDOS.get(projectDOS.size() - 1).getId());
                build.setProjectName(projectDOS.get(projectDOS.size() - 1).getProjectName());
            }
            resultVOS.add(build);
        });
        return ResponsePageResult.success(resultVOS, new PageInfo(deviceDOPage.getCurrent(), deviceDOPage.getSize(), deviceDOPage.getTotal(), deviceDOPage.getPages()));

    }


    @Override
    public void deviceTodayWorkTimeFromCloudm() {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        int page = 1;
        int size = 50;
        while (true) {
            Page<DeviceDO> deviceDOPage = deviceMapper.selectPage(new Page<>(page, size),
                    new LambdaQueryWrapper<DeviceDO>()
                            .eq(DeviceDO::getIsDeleted, 0));
            if (CollectionUtils.isEmpty(deviceDOPage.getRecords())) {
                break;
            }
            deviceDOPage.getRecords().forEach(deviceDO -> {
                // 从云机械获取设备当天工时
                try {
                    ResponseResult<List<WorkTimeListDTO>> listResponseResult = deviceTimeService.workTimeList(deviceDO.getDeviceSn(), today, today);
                    if (null == listResponseResult || WitmoreErrorCode.SUCCESS.getCode() != listResponseResult.getCode()
                            || null == listResponseResult.getData()) {
                        return;
                    }
                    List<WorkTimeListDTO> workTimeListDTOS = listResponseResult.getData();
                    if (CollectionUtils.isEmpty(workTimeListDTOS)) {
                        return;
                    }
                    WorkTimeListDTO workTimeListDTO = workTimeListDTOS.get(0);
                    double todayWorkTime = null == workTimeListDTO.getWorkTime() ? 0 : workTimeListDTO.getWorkTime().doubleValue();
                    DeviceDO build = DeviceDO.builder()
                            .id(deviceDO.getId())
                            .todayWorkTime(todayWorkTime)
                            .idleTime(workTimeListDTO.getIdleTime())
                            .loadWorkTime(workTimeListDTO.getLoadWorkTime())
                            .updateTime(LocalDateTime.now())
                            .build();
                    deviceMapper.updateById(build);
                } catch (Exception e) {
                    log.error("设备当天工时获取失败 error msg-->{},deviceDO-->{}, e",
                            e.getMessage(), JSONObject.toJSONString(deviceDO), e);
                }
            });
            // 翻到下一页
            page++;
        }
    }

    @Override
    public void deviceTodayWorkTimeReset() {
        int page = 1;
        int size = 50;
        while (true) {
            Page<DeviceDO> deviceDOPage = deviceMapper.selectPage(new Page<>(page, size), new QueryWrapper<DeviceDO>().eq("is_deleted", 0));
            if (CollectionUtils.isEmpty(deviceDOPage.getRecords())) {
                break;
            }
            deviceDOPage.getRecords().forEach(deviceDO -> {
                DeviceDO build = DeviceDO.builder()
                        .id(deviceDO.getId())
                        .todayWorkTime(0.0)
                        .updateTime(LocalDateTime.now())
                        .build();
                deviceMapper.updateById(build);
            });
            // 翻到下一页
            page++;
        }
    }

    @Override
    public ResponseResult<List<DeviceCategoryVO>> deviceCategoryDistribution(AccountSession accountSession) {
        // 根据项目部id查询设备id信息
        List<Integer> businessIdListByProjectId = new ArrayList<>();
        if (null != accountSession.getProjectId()) {
            businessIdListByProjectId = projectService.getBusinessIdListByProjectId(accountSession.getOrgId(), accountSession.getProjectId(), 1);
            if (CollectionUtils.isEmpty(businessIdListByProjectId)) {
                return ResponseResult.success(Collections.emptyList());
            }
        }
        List<DeviceCategoryResultDTO> deviceCategoryResultDTOS = deviceMapper.deviceCategoryDistribution(accountSession.getOrgId(), businessIdListByProjectId);
        if (CollectionUtils.isEmpty(deviceCategoryResultDTOS)) {
            return ResponseResult.success(Collections.emptyList());
        }
        List<DeviceCategoryVO> resultVOS = new ArrayList<>();
        deviceCategoryResultDTOS.forEach(deviceStatusResultDTO -> {
            DeviceCategoryVO build = DeviceCategoryVO.builder()
                    .category(deviceStatusResultDTO.getCategoryName())
                    .total(deviceStatusResultDTO.getTotal())
                    .build();
            resultVOS.add(build);
        });
        return ResponseResult.success(resultVOS);
    }


    @Override
    public ResponsePageResult<DeviceDailyResultVO> deviceDailyReportPage(DeviceReportParam deviceDailyParamVO) {
        if (deviceDailyParamVO.getSkylineDeviceId() == null) {
            return ResponsePageResult.fail(WitmoreErrorCode.PARAM_ERROR);
        }
        // 转换时间
        LocalDate startDay = deviceDailyParamVO.getStartDate();
        if (null == startDay) {
            startDay = LocalDate.now().minusMonths(1);
        }
        LocalDate endDay = deviceDailyParamVO.getEndDate();
        if (null == endDay) {
            endDay = LocalDate.now();
        }
        if (startDay.isAfter(endDay)) {
            return ResponsePageResult.fail(WitmoreErrorCode.START_DATE_AFTER_END_DATE);
        }
        // 根据项目部id查询设备id信息
        List<Integer> businessIdListByProjectId = new ArrayList<>();
        businessIdListByProjectId.add(deviceDailyParamVO.getSkylineDeviceId());
        if (null != deviceDailyParamVO.getProjectId()) {
            businessIdListByProjectId = projectService.getBusinessIdListByProjectId(deviceDailyParamVO.getOrgId(), deviceDailyParamVO.getProjectId(), 1);
            if (CollectionUtils.isEmpty(businessIdListByProjectId)) {
                return ResponsePageResult.success(new ArrayList<>(),
                        new PageInfo(deviceDailyParamVO.getPage(), deviceDailyParamVO.getSize(), 0, 0));
            }
        }
        Page<DeviceDailyReportDO> deviceDailyReportDOPage = deviceDailyReportMapper.selectPage(new Page<>(deviceDailyParamVO.getPage(), deviceDailyParamVO.getSize()),
                new LambdaQueryWrapper<DeviceDailyReportDO>()
                        .eq(DeviceDailyReportDO::getOrgId, deviceDailyParamVO.getOrgId())
                        .in(!ObjectUtils.isEmpty(businessIdListByProjectId), DeviceDailyReportDO::getSkylineDeviceId, businessIdListByProjectId)
                        .between(DeviceDailyReportDO::getReportDate, startDay, endDay)
                        .orderByDesc(DeviceDailyReportDO::getReportDate));
        if (deviceDailyReportDOPage == null) {
            return ResponsePageResult.fail(WitmoreErrorCode.SYSTEM_ERROR);
        }
        List<DeviceDailyResultVO> deviceDailyResultVOS = new ArrayList<>();
        deviceDailyReportDOPage.getRecords().forEach(deviceDailyReportDO -> {
            DeviceDailyResultVO build = DeviceDailyResultVO.builder().build();
            BeanUtils.copyProperties(deviceDailyReportDO, build);
            // 时间格式转换
            build.setReportDateStr(LocalDateUtils.dateConvertString(deviceDailyReportDO.getReportDate()));
            if (null != deviceDailyReportDO.getEarlyStartTime()) {
                build.setEarlyStartTimeStr(LocalDateUtils.dateConvertDateTime(deviceDailyReportDO.getEarlyStartTime()));
            }
            if (null != deviceDailyReportDO.getLatestEndTime()) {
                build.setLatestEndTimeStr(LocalDateUtils.dateConvertDateTime(deviceDailyReportDO.getLatestEndTime()));
            }
            // 填充项目信息 - 直接从设备表获取项目ID
            DeviceDO deviceDO = deviceMapper.selectOne(new LambdaQueryWrapper<DeviceDO>()
                    .eq(DeviceDO::getId, deviceDailyReportDO.getSkylineDeviceId())
                    .eq(DeviceDO::getIsDeleted, 0));
            if (deviceDO == null || deviceDO.getProjectId() == null) {
                // 没有挂在项目部下面 说明直接挂在企业下面的
                OrgDO orgDO = orgMapper.selectById(deviceDailyParamVO.getOrgId());
                build.setProjectNames(null == orgDO ? new ArrayList<>() : Collections.singletonList(orgDO.getOrgName()));
            } else {
                // 向上查询项目部的路径
                String projectPath = projectService.getProjectNamesPathByProjectId(deviceDO.getProjectId());
                build.setProjectNames(Collections.singletonList(projectPath));
            }
            deviceDailyResultVOS.add(build);
        });
        return ResponsePageResult.success(deviceDailyResultVOS,
                new PageInfo(deviceDailyReportDOPage.getCurrent(), deviceDailyReportDOPage.getSize(), deviceDailyReportDOPage.getTotal(), deviceDailyReportDOPage.getPages()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseResult<Void> edit(AccountSession accountInfo, DeviceEditParam deviceEditParam) {
        DeviceDO deviceDO = deviceMapper.selectOne(new LambdaQueryWrapper<DeviceDO>()
                .eq(DeviceDO::getDeviceSn, deviceEditParam.getDeviceSn())
                .eq(DeviceDO::getOrgId, accountInfo.getOrgId())
                .eq(DeviceDO::getIsDeleted, 0)
        );
        if (deviceDO == null) {
            return ResponseResult.fail(WitmoreErrorCode.PARAM_ERROR);
        }

        // 处理驾驶员绑定逻辑
        Integer newDriverId = deviceEditParam.getDriverId();
        Integer currentDriverId = deviceDO.getDriverId();
        String driverName;
        String driverPhone;
        LocalDateTime bindTime;

        // 判断驾驶员是否发生变化
        if (Objects.equals(currentDriverId, newDriverId)) {
            // 驾驶员没有变化，保持原有信息
            driverName = deviceDO.getDriverName();
            driverPhone = deviceDO.getDriverPhone();
            bindTime = deviceDO.getBindTime();
        } else if (newDriverId == null) {
            // 解绑操作（设置为null或从有绑定变为无绑定）
            driverName = null;
            driverPhone = null;
            bindTime = null;
        } else {
            // 绑定新驾驶员（新绑定或更换驾驶员）
            // 校验驾驶员是否存在且属于当前企业
            DriverDO driver = driverMapper.selectById(newDriverId);
            if (driver == null || !driver.getOrgId().equals(accountInfo.getOrgId()) || driver.getIsDeleted() == 1) {
                return ResponseResult.fail(400, "驾驶员不存在或不属于当前企业");
            }

            // 检查驾驶员是否已绑定其他设备（排除当前设备）
            LambdaQueryWrapper<DeviceDO> driverCheckWrapper = new LambdaQueryWrapper<>();
            driverCheckWrapper.eq(DeviceDO::getDriverId, newDriverId)
                             .eq(DeviceDO::getIsDeleted, 0)
                             .ne(DeviceDO::getId, deviceDO.getId());
            DeviceDO existDevice = deviceMapper.selectOne(driverCheckWrapper);
            if (existDevice != null) {
                return ResponseResult.fail(400, "该驾驶员已绑定其他设备，请先解绑");
            }

            // 设置新的驾驶员信息
            driverName = driver.getDriverName();
            driverPhone = driver.getPhone();
            bindTime = LocalDateTime.now();
        }

        // 更新设备信息，包括项目部ID和驾驶员信息
        LambdaUpdateWrapper<DeviceDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(DeviceDO::getId, deviceDO.getId())
                    .set(DeviceDO::getDeviceName, deviceEditParam.getDeviceName())
                    .set(DeviceDO::getModel, deviceEditParam.getModel())
                    .set(DeviceDO::getBrand, deviceEditParam.getBrand())
                    .set(DeviceDO::getCategory, deviceEditParam.getCategory())
                    .set(DeviceDO::getProjectId, deviceEditParam.getProjectId())
                    .set(DeviceDO::getDriverId, newDriverId)
                    .set(DeviceDO::getDriverName, driverName)
                    .set(DeviceDO::getDriverPhone, driverPhone)
                    .set(DeviceDO::getBindTime, bindTime)
                    .set(DeviceDO::getFuelTankCapacity, deviceEditParam.getFuelTankCapacity())
                    .set(DeviceDO::getHasOilLevelModule, deviceEditParam.getHasOilLevelModule())
                    .set(DeviceDO::getUpdateTime, LocalDateTime.now());

        deviceMapper.update(null, updateWrapper);
        List<CameraDO> cameraDOS = cameraMapper.selectList(new LambdaQueryWrapper<CameraDO>()
                .eq(CameraDO::getSkylineDeviceId, deviceDO.getId())
                .eq(CameraDO::getIsDeleted, 0)
                .eq(CameraDO::getOrgId, accountInfo.getOrgId())
        );

        if (CollectionUtils.isEmpty(cameraDOS)) {
            return ResponseResult.success();
        }
        // 同步更新该设备下所有摄像头的项目部ID
        cameraDOS.forEach(cameraDO -> {
            CameraDO updateCamera = new CameraDO();
            updateCamera.setId(cameraDO.getId());
            updateCamera.setProjectId(deviceEditParam.getProjectId());
            updateCamera.setUpdateTime(LocalDateTime.now());
            cameraMapper.updateById(updateCamera);
        });
        return ResponseResult.success();
    }


    /**
     * 保存设备数据
     */
    @Override
    public void saveDeviceList(List<CloudmDeviceDTO> devices) {
        if (CollectionUtils.isEmpty(devices)) {
            return;
        }
        for (CloudmDeviceDTO device : devices) {
            try {
                // 一条一条入库
                // 处理油箱容量信息
                Float fuelTankCapacity = null;
                if (device.getDeviceOilList() != null && !device.getDeviceOilList().isEmpty()) {
                    fuelTankCapacity = device.getDeviceOilList().get(0).getDeviceFuelTankage();
                }

                // 验证库内是否已有数据
                List<DeviceDO> deviceDOS = deviceMapper.selectList(new LambdaQueryWrapper<DeviceDO>()
                        .eq(DeviceDO::getDeviceSn, device.getSnId())
                );
                if (ObjectUtils.isNotEmpty(deviceDOS)) {
//                    // 设备已存在，更新油箱容量信息 TODO 仅第一次进行油箱容量同步 后续稳定后删除代码
//                    DeviceDO existingDevice = deviceDOS.get(0);
//                    DeviceDO updateDevice = DeviceDO.builder()
//                            .id(existingDevice.getId())
//                            .fuelTankCapacity(fuelTankCapacity)
//                            .updateTime(LocalDateTime.now())
//                            .build();
//                    deviceMapper.updateById(updateDevice);
                    continue;
                }

                // 库内没有数据 直接入库，默认设置项目部ID为9999
                DeviceDO build = DeviceDO.builder()
                        .thirdDeviceId(device.getId())
                        .deviceSn(device.getSnId())
                        .deviceName(device.getDeviceName())
                        .orgId(1)
                        .projectId(9999) // 默认挂在 projectId = 9999 表示默认项目
                        .brand(device.getBrand())
                        .model(device.getModel())
                        .category(device.getCategory())
                        .workTime(device.getWorkTime())
                        .fuelTankCapacity(fuelTankCapacity) // 设置油箱容量
                        .createTime(LocalDate.parse(device.getCreateTime()))
                        .updateTime(LocalDateTime.now())
                        .isDeleted(0)
                        .build();
                deviceMapper.insert(build);
            } catch (Exception e) {
                log.error("当前设备保存或更新失败 error msg-->{},device-->{}, e",
                        e.getMessage(), JSONObject.toJSONString(device), e);
            }
        }
    }

    @Autowired
    private ProjectMapper projectMapper;

    @Override
    public Map<String, Integer> cameraPicFix(String cameraSn) {
        List<CameraDO> cameraDOS = cameraMapper.selectList(new LambdaQueryWrapper<CameraDO>()
                .eq(ObjectUtils.isNotEmpty(cameraSn), CameraDO::getCameraSn, cameraSn));
        if (CollectionUtils.isEmpty(cameraDOS)) {
            return Map.of();
        }
        cameraDOS.forEach(cameraDO -> {
            // 直接从摄像头表获取项目ID
            if (cameraDO.getProjectId() == null) {
                return;
            }
            ProjectDO projectDO = projectMapper.selectById(cameraDO.getProjectId());
            if (projectDO == null) {
                return;
            }
            CameraPicDO cameraPicDO = CameraPicDO.builder()
                    .projectId(cameraDO.getProjectId())
                    .projectName(projectDO.getProjectName())
                    .build();
            cameraPicMapper.update(cameraPicDO, new LambdaQueryWrapper<CameraPicDO>()
                    .eq(CameraPicDO::getSkylineCameraId, cameraDO.getId())
                    .eq(CameraPicDO::getCameraSn, cameraDO.getCameraSn())
            );
        });

        return Map.of();
    }


    private void totalRefuelByDay(LocalDate startDate, LocalDate endDate, DeviceDO deviceDO) {
        // 循环遍历日期
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            DeviceOilListParamVO deviceOilListParamVO = DeviceOilListParamVO.builder()
                    .deviceSn(deviceDO.getDeviceSn())
                    .startTime(date.atStartOfDay())
                    .endTime(date.atTime(23, 59, 59))
                    .build();
            ResponseResult<AddOilDTO> addOilDTOResponseResult = deviceOilService.refuelList(deviceOilListParamVO);
            if (null == addOilDTOResponseResult || WitmoreErrorCode.SUCCESS.getCode() != addOilDTOResponseResult.getCode()
                    || null == addOilDTOResponseResult.getData()) {
                continue;
            }
            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .oilAdd(addOilDTOResponseResult.getData().getAddOilTotal())
                    .build();
            this.saveOrUpdateDeviceDailyReport(deviceDO, date, build);
        }
    }

    private void totalRefuelByDayCustom(LocalDate startDate, LocalDate endDate, DeviceDO deviceDO) {
        // 循环遍历日期
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            DeviceOilListParamVO deviceOilListParamVO = DeviceOilListParamVO.builder()
                    .deviceSn(deviceDO.getDeviceSn())
                    .startTime(date.minusDays(3).atStartOfDay())
                    .endTime(date.atTime(23, 59, 59))
                    .build();
            // 改用V2接口（带缓存和分页优化）
            ResponseResult<List<DeviceOilDTO>> listResponseResult = deviceOilService.oilListV2(deviceOilListParamVO);
            if (null == listResponseResult || WitmoreErrorCode.SUCCESS.getCode() != listResponseResult.getCode()
                    || null == listResponseResult.getData()) {
                continue;
            }
            // 按照 时间 升序
            List<DeviceOilDTO> resultData = listResponseResult.getData();
            resultData.sort(Comparator.comparing(DeviceOilDTO::getTime));
            OilAnalyzer.OilDetail oilPercent = OilAnalyzer.getOilPercent(resultData);
            if (CollectionUtils.isEmpty(oilPercent.getUpDataList())){
                continue;
            }
            // 遍历 累加加油量
            LocalDate finalDate = date;
            double addOilTotal = oilPercent.getUpDataList().stream()
                    // 先筛选出 addOilTimeL 的日期等于指定 date 的记录
                    .filter(info -> info.getAddOilTimeL() != null
                            && info.getAddOilTimeL().toLocalDate().equals(finalDate))
                    // 再做金额计算
                    .mapToDouble(info -> {
                        double tank = deviceDO.getFuelTankCapacity() == null
                                ? 0.0
                                : deviceDO.getFuelTankCapacity();
                        return (info.getOilCharge() * tank) / 100.0;
                    })
                    .sum();

            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .oilAddCustom(addOilTotal)
                    .build();
            this.saveOrUpdateDeviceDailyReport(deviceDO, date, build);
        }
    }

    /**
     * 保存或更新设备日报数据
     */
    private void saveOrUpdateDeviceDailyReport(DeviceDO deviceDO, LocalDate date, DeviceDailyReportDO reportDO) {
        // 查询日报数据 当前设备当前日期下是否拉过数据
        DeviceDailyReportDO deviceDailyReportDO = deviceDailyReportMapper.selectOne(new QueryWrapper<DeviceDailyReportDO>()
                .eq("skyline_device_id", deviceDO.getId())
                .eq("report_date", date));

        // 获取项目信息
        Integer projectId = deviceDO.getProjectId();
        String projectName = null;

        if (projectId != null) {
            ProjectDO project = projectMapper.selectById(projectId);
            if (project != null) {
                projectName = project.getProjectName();
            }
        }

        // TODO 后续可改为有则更新无则插入
        if (null != deviceDailyReportDO) {
            // 更新
            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .id(deviceDailyReportDO.getId())
                    .projectId(projectId)
                    .projectName(projectName)
                    .oilAdd(reportDO.getOilAdd())
                    .oilAddCustom(reportDO.getOilAddCustom())
                    .workTime(reportDO.getWorkTime())
                    .idleTime(reportDO.getIdleTime())
                    .loadWorkTime(reportDO.getLoadWorkTime())
                    .earlyStartTime(reportDO.getEarlyStartTime())
                    .latestEndTime(reportDO.getLatestEndTime())
                    .oilConsumption(reportDO.getOilConsumption())
                    .oilConsumptionAverage(reportDO.getOilConsumptionAverage())
                    .updateTime(LocalDateTime.now())
                    .build();
            deviceDailyReportMapper.updateById(build);
            return;
        }
        DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                .reportDate(date)
                .skylineDeviceId(deviceDO.getId())
                .orgId(deviceDO.getOrgId())
                .projectId(projectId)
                .projectName(projectName)
                .oilAdd(reportDO.getOilAdd())
                .workTime(reportDO.getWorkTime())
                .idleTime(reportDO.getIdleTime())
                .loadWorkTime(reportDO.getLoadWorkTime())
                .earlyStartTime(reportDO.getEarlyStartTime())
                .latestEndTime(reportDO.getLatestEndTime())
                .oilConsumption(reportDO.getOilConsumption())
                .oilConsumptionAverage(reportDO.getOilConsumptionAverage())
                .updateTime(LocalDateTime.now())
                .createTime(LocalDateTime.now())
                .build();
        deviceDailyReportMapper.insert(build);
    }

    /**
     * 总工时
     */
    private void totalWorkTimeByDay(String startDay, String endDay, DeviceDO deviceDO) throws Exception {
        List<WorkTimeListDTO> workTimeListDTOS = apiDeviceDataClient.workTimeList(deviceDO.getDeviceSn(), startDay, endDay);
        if (CollectionUtils.isEmpty(workTimeListDTOS)) {
            return;
        }
        for (WorkTimeListDTO workTimeListDTO : workTimeListDTOS) {
            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .workTime(null == workTimeListDTO.getWorkTime() ? 0.0 : workTimeListDTO.getWorkTime().doubleValue())
                    .idleTime(workTimeListDTO.getIdleTime())
                    .loadWorkTime(workTimeListDTO.getLoadWorkTime())
                    .build();
            this.saveOrUpdateDeviceDailyReport(deviceDO, LocalDate.parse(workTimeListDTO.getDate()), build);
        }
    }

    /**
     * 最早开始时间、最晚结束时间
     */
    private void earlyAndLateWorkTime(LocalDate startDate, LocalDate endDate, DeviceDO deviceDO) {
        // 循环遍历日期
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            ResponseResult<List<WorkDetailDTO>> listResponseResult = deviceTimeService.workTimeDetails(deviceDO.getDeviceSn(), date);
            if (null == listResponseResult || WitmoreErrorCode.SUCCESS.getCode() != listResponseResult.getCode()
                    || null == listResponseResult.getData()) {
                continue;
            }
            List<WorkDetailDTO> workDetailDTOS = listResponseResult.getData();
            // 根据列表筛选窜出 时间段内 最早开始时间  和 最晚结束时间
            WorkDetailDTO early = workDetailDTOS.stream()
                    .filter(workDetailDTO -> StringUtils.isNotEmpty(workDetailDTO.getStartTime()))
                    .min(Comparator.comparing(WorkDetailDTO::getStartTime))
                    .orElse(null);

            WorkDetailDTO late = workDetailDTOS.stream()
                    .filter(workDetailDTO -> StringUtils.isNotEmpty(workDetailDTO.getEndTime()))
                    .max(Comparator.comparing(WorkDetailDTO::getEndTime))
                    .orElse(null);

            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .earlyStartTime(LocalDateUtils.stringConvertDateTime(null == early ? null : early.getStartTime()))
                    .latestEndTime(LocalDateUtils.stringConvertDateTime(null == late ? null : late.getEndTime()))
                    .build();
            this.saveOrUpdateDeviceDailyReport(deviceDO, date, build);
        }

    }

    /**
     * 每日报表-用油量
     */
    private void totalOilConsumption(LocalDate startDate, LocalDate endDate, DeviceDO deviceDO) {
        // 循环遍历日期
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            DeviceOilListParamVO deviceOilListParamVO = DeviceOilListParamVO.builder()
                    .deviceSn(deviceDO.getDeviceSn())
                    .startTime(date.atStartOfDay())
                    .endTime(date.atTime(23, 59, 59))
                    .build();
            ResponseResult<List<DeviceOilConsumptionDTO>> listResponseResult = deviceOilService.oilConsumptionList(deviceOilListParamVO);
            if (null == listResponseResult || WitmoreErrorCode.SUCCESS.getCode() != listResponseResult.getCode()
                    || null == listResponseResult.getData()) {
                continue;
            }
            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .oilConsumption(listResponseResult.getData().get(0).getOilConsumption())
                    .build();
            this.saveOrUpdateDeviceDailyReport(deviceDO, date, build);
        }
    }

    private void avgFuelConsumption(LocalDate startDate, LocalDate endDate, DeviceDO deviceDO) {
        // 循环遍历日期
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            DeviceOilListParamVO deviceOilListParamVO = DeviceOilListParamVO.builder()
                    .deviceSn(deviceDO.getDeviceSn())
                    .startTime(date.atStartOfDay())
                    .endTime(date.atTime(23, 59, 59))
                    .build();
            ResponseResult<List<DeviceAvgFuelConsumptionDTO>> listResponseResult = deviceOilService.avgFuelConsumptionList(deviceOilListParamVO);
            if (null == listResponseResult || WitmoreErrorCode.SUCCESS.getCode() != listResponseResult.getCode()
                    || null == listResponseResult.getData()) {
                continue;
            }
            DeviceDailyReportDO build = DeviceDailyReportDO.builder()
                    .oilConsumptionAverage(listResponseResult.getData().get(0).getAvgFuelConsumption())
                    .build();
            this.saveOrUpdateDeviceDailyReport(deviceDO, date, build);
        }
    }

    @Override
    public ResponseResult<List<DeviceMapStatisticsVO>> deviceMapStatistics(AccountSession accountSession) {
        try {
            // 查询设备地图统计数据
            List<DeviceMapStatisticsResultDTO> resultList = deviceMapper.deviceMapStatistics(accountSession.getOrgId());

            // 转换为VO对象
            List<DeviceMapStatisticsVO> voList = resultList.stream().map(dto -> {
                DeviceMapStatisticsVO vo = new DeviceMapStatisticsVO();
                vo.setCity(dto.getCity());
                vo.setProvince(dto.getProvince());
                vo.setDeviceCount(dto.getDeviceCount());
                return vo;
            }).collect(Collectors.toList());

            return ResponseResult.success(voList);
        } catch (Exception e) {
            log.error("查询设备地图统计数据失败", e);
            return ResponseResult.fail(WitmoreErrorCode.SYSTEM_ERROR.getCode(), "查询设备地图统计数据失败");
        }
    }

    @Override
    public ResponseResult<DeviceDetailResultVO> deviceDetail(DeviceSnParam deviceSnParam, AccountSession accountSession) {
        try {
            // 查询设备基本信息
            DeviceDO deviceDO = deviceMapper.selectOne(new LambdaQueryWrapper<DeviceDO>()
                    .eq(DeviceDO::getDeviceSn, deviceSnParam.getDeviceSn())
                    .eq(DeviceDO::getIsDeleted, 0));

            if (deviceDO == null) {
                return ResponseResult.fail(WitmoreErrorCode.NO_DATA.getCode(), "设备不存在");
            }

            // 构建返回对象
            DeviceDetailResultVO.DeviceDetailResultVOBuilder builder = DeviceDetailResultVO.builder()
                    .id(deviceDO.getId())
                    .thirdDeviceId(deviceDO.getThirdDeviceId())
                    .deviceSn(deviceDO.getDeviceSn())
                    .deviceName(deviceDO.getDeviceName())
                    .orgId(deviceDO.getOrgId())
                    .brand(deviceDO.getBrand())
                    .model(deviceDO.getModel())
                    .category(deviceDO.getCategory())
                    .currentAddr(deviceDO.getCurrentAddr())
                    .province(deviceDO.getProvince())
                    .city(deviceDO.getCity())
                    .district(deviceDO.getDistrict())
                    .lng(deviceDO.getLng())
                    .lat(deviceDO.getLat())
                    .workStatus(deviceDO.getWorkStatus())
                    .workTime(deviceDO.getWorkTime())
                    .todayWorkTime(deviceDO.getTodayWorkTime())
                    .createTime(deviceDO.getCreateTime())
                    .oilLevel(deviceDO.getOilLevel())
                    .fuelTankCapacity(deviceDO.getFuelTankCapacity())
                    .hasOilLevelModule(deviceDO.getHasOilLevelModule())
                    .isDeleted(deviceDO.getIsDeleted())
                    .updateTime(deviceDO.getUpdateTime())
                    // 驾驶员相关信息
                    .driverId(deviceDO.getDriverId())
                    .driverName(deviceDO.getDriverName())
                    .driverPhone(deviceDO.getDriverPhone())
                    .bindTime(deviceDO.getBindTime());

            // 如果有绑定驾驶员，查询驾驶员详细信息
            if (deviceDO.getDriverId() != null) {
                DriverDO driver = driverMapper.selectById(deviceDO.getDriverId());
                if (driver != null && driver.getIsDeleted() == 0) {
                    builder.driverPhoto(driver.getDriverPhoto());
                    builder.licenseType(driver.getLicenseType());
                }
            }

            // 填充项目信息
            List<ProjectDO> projectDOS = projectService.getProjectParentListByBusinessId(deviceDO.getId(), 1);
            if (!CollectionUtils.isEmpty(projectDOS)) {
                builder.projectParentIdsList(projectDOS.stream().map(ProjectDO::getId).collect(Collectors.toList()));
                builder.projectParentNames(projectDOS.stream().map(ProjectDO::getProjectName).collect(Collectors.toList()));
                builder.projectId(projectDOS.get(projectDOS.size() - 1).getId());
                builder.projectName(projectDOS.get(projectDOS.size() - 1).getProjectName());
            }

            return ResponseResult.success(builder.build());
        } catch (Exception e) {
            log.error("查询设备概况信息失败", e);
            return ResponseResult.fail(WitmoreErrorCode.SYSTEM_ERROR.getCode(), "查询设备概况信息失败");
        }
    }

    @Override
    public ResponseResult<List<DeviceMonthlyUsageRateVO>> deviceMonthlyUsageRate(AccountSession accountSession, String category) {
        try {
            // 1. 计算时间范围：当月1号到昨天
            LocalDate currentDate = LocalDate.now();
            LocalDate yesterday = currentDate.minusDays(1);
            LocalDate startDate = currentDate.withDayOfMonth(1);
            LocalDate endDate = yesterday;

            // 2. 边界情况检查：如果昨天不是当月，返回空结果
            if (yesterday.getMonth() != currentDate.getMonth() ||
                yesterday.getYear() != currentDate.getYear()) {
                log.info("当前是当月第一天，昨天不在当月范围内，返回空结果");
                return ResponseResult.success(new ArrayList<>());
            }

            // 3. 计算总天数：从当月1号到昨天
            int totalDays = (int) ChronoUnit.DAYS.between(startDate, endDate) + 1;

            log.info("查询设备当月使用率，时间范围：{} 到 {}，总天数：{}", startDate, endDate, totalDays);

            // 4. 查询数据
            List<DeviceMonthlyUsageRateDTO> dataList = deviceDailyReportMapper
                .selectDeviceMonthlyUsageRate(
                    accountSession.getOrgId(),
                    startDate.toString(),
                    endDate.toString(),
                    businessConfig.getLoadWorkTimeThreshold(), // 工作时长阈值：从配置文件读取
                    category
                );

            // 5. 计算使用率并构建VO
            List<DeviceMonthlyUsageRateVO> voList = dataList.stream()
                .map(dto -> {
                    BigDecimal usageRate = BigDecimal.ZERO;
                    if (totalDays > 0 && dto.getWorkingDays() != null) {
                        usageRate = new BigDecimal(dto.getWorkingDays())
                            .multiply(new BigDecimal("100"))
                            .divide(new BigDecimal(totalDays), 2, RoundingMode.HALF_UP);
                    }

                    return DeviceMonthlyUsageRateVO.builder()
                        .deviceId(dto.getDeviceId())
                        .deviceName(dto.getDeviceName())
                        .deviceSn(dto.getDeviceSn())
                        .category(dto.getCategory())
                        .projectId(dto.getProjectId())
                        .projectName(dto.getProjectName())
                        .workingDays(dto.getWorkingDays() != null ? dto.getWorkingDays() : 0)
                        .totalDays(totalDays)
                        .usageRate(usageRate)
                        .build();
                })
                // 6. 按使用率倒序排列（使用率高的排在前面）
                .sorted((vo1, vo2) -> {
                    // 首先按使用率倒序排列
                    int usageRateCompare = vo2.getUsageRate().compareTo(vo1.getUsageRate());
                    if (usageRateCompare != 0) {
                        return usageRateCompare;
                    }
                    // 使用率相同时，按工作天数倒序排列
                    int workingDaysCompare = vo2.getWorkingDays().compareTo(vo1.getWorkingDays());
                    if (workingDaysCompare != 0) {
                        return workingDaysCompare;
                    }
                    // 工作天数也相同时，按设备名称升序排列
                    return vo1.getDeviceName().compareTo(vo2.getDeviceName());
                })
                .collect(Collectors.toList());

            log.info("查询设备当月使用率成功，共查询到 {} 台设备", voList.size());
            return ResponseResult.success(voList);

        } catch (Exception e) {
            log.error("查询设备当月使用率失败", e);
            return ResponseResult.fail(WitmoreErrorCode.SYSTEM_ERROR.getCode(), "查询设备当月使用率失败");
        }
    }

    @Override
    public ResponseResult<Void> bindDriver(DeviceBindDriverParam param, AccountSession accountSession) {
        try {
            // 1. 校验设备是否存在且属于当前企业
            DeviceDO device = deviceMapper.selectById(param.getDeviceId());
            if (device == null || !device.getOrgId().equals(accountSession.getOrgId())) {
                return ResponseResult.fail(404, "设备不存在");
            }

            // 2. 校验驾驶员是否存在且属于当前企业
            DriverDO driver = driverMapper.selectById(param.getDriverId());
            if (driver == null || !driver.getOrgId().equals(accountSession.getOrgId())) {
                return ResponseResult.fail(404, "驾驶员不存在");
            }

            // 3. 检查设备是否已绑定其他驾驶员
            if (device.getDriverId() != null) {
                return ResponseResult.fail(400, "该设备已绑定其他驾驶员，请先解绑");
            }

            // 4. 检查驾驶员是否已绑定其他设备
            LambdaQueryWrapper<DeviceDO> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(DeviceDO::getDriverId, param.getDriverId())
                   .eq(DeviceDO::getIsDeleted, 0);
            DeviceDO existDevice = deviceMapper.selectOne(wrapper);
            if (existDevice != null) {
                return ResponseResult.fail(400, "该驾驶员已绑定其他设备，请先解绑");
            }

            // 5. 更新设备信息
            DeviceDO updateDevice = DeviceDO.builder()
                    .id(param.getDeviceId())
                    .driverId(param.getDriverId())
                    .driverName(driver.getDriverName())
                    .driverPhone(driver.getPhone())
                    .bindTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();

            int result = deviceMapper.updateById(updateDevice);
            if (result > 0) {
                log.info("设备绑定驾驶员成功，设备ID：{}，驾驶员ID：{}", param.getDeviceId(), param.getDriverId());
                return ResponseResult.success();
            } else {
                return ResponseResult.fail(500, "绑定失败");
            }

        } catch (Exception e) {
            log.error("绑定设备和驾驶员失败", e);
            return ResponseResult.fail(500, "绑定设备和驾驶员失败");
        }
    }

    @Override
    public ResponseResult<Void> unbindDriver(Integer deviceId, AccountSession accountSession) {
        try {
            // 1. 校验设备是否存在且属于当前企业
            DeviceDO device = deviceMapper.selectById(deviceId);
            if (device == null || !device.getOrgId().equals(accountSession.getOrgId())) {
                return ResponseResult.fail(404, "设备不存在");
            }

            // 2. 检查设备是否已绑定驾驶员
            if (device.getDriverId() == null) {
                return ResponseResult.fail(400, "该设备未绑定驾驶员");
            }

            // 3. 解绑驾驶员
            LambdaUpdateWrapper<DeviceDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(DeviceDO::getId, deviceId)
                        .set(DeviceDO::getDriverId, null)
                        .set(DeviceDO::getDriverName, null)
                        .set(DeviceDO::getDriverPhone, null)
                        .set(DeviceDO::getBindTime, null)
                        .set(DeviceDO::getUpdateTime, LocalDateTime.now());

            int result = deviceMapper.update(null, updateWrapper);
            if (result > 0) {
                log.info("设备解绑驾驶员成功，设备ID：{}", deviceId);
                return ResponseResult.success();
            } else {
                return ResponseResult.fail(500, "解绑失败");
            }

        } catch (Exception e) {
            log.error("解绑设备和驾驶员失败", e);
            return ResponseResult.fail(500, "解绑设备和驾驶员失败");
        }
    }

    @Override
    public ResponseResult<DriverSimpleVO> getDriverByDevice(Integer deviceId) {
        try {
            DeviceDO device = deviceMapper.selectById(deviceId);
            if (device == null || device.getIsDeleted() == 1) {
                return ResponseResult.fail(404, "设备不存在");
            }

            if (device.getDriverId() == null) {
                return ResponseResult.success(null);
            }

            DriverDO driver = driverMapper.selectById(device.getDriverId());
            if (driver == null || driver.getIsDeleted() == 1) {
                return ResponseResult.success(null);
            }

            DriverSimpleVO vo = DriverSimpleVO.builder()
                    .id(driver.getId())
                    .driverName(driver.getDriverName())
                    .phone(driver.getPhone())
                    .driverPhoto(driver.getDriverPhoto())
                    .licenseType(driver.getLicenseType())
                    .build();

            return ResponseResult.success(vo);

        } catch (Exception e) {
            log.error("查询设备绑定的驾驶员失败", e);
            return ResponseResult.fail(500, "查询设备绑定的驾驶员失败");
        }
    }

}
